<?php
/**
 * Copyright (c) Company 贵州汉图数通信息技术有限公司
 * Department 研发&产品设计部-研发部
 * Author: starsphp
 * Created by PhpStorm
 * Date 2021-12-1
 * Time 15:11
 */

namespace app\im\controller;

use app\common\model\im\MessageModel;
use app\common\model\im\RecentSessionModel;
use GatewayWorker\Lib\Gateway;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Db;
use think\Model;


/**
 * im 消息聊天 基础类
 * Class BaseController
 * @packge app\im\controller
 **/
class BaseController
{
    /**
     * 客户端类型 $clientType
     * @var string[]
     */
    protected static array $clientType;
    
    /**
     * 移动端类型 对应的map [ uid 键名pk, 类型type ]
     * @var string[]
     */
    protected static array $clientMap;
    
    /**
     * 服务启动时触发
     * 初始化一些必要的条件
     */
    public static function onWorkerStart()
    {
        var_dump('启动成功：' . date('Y-m-d H:i:s'));
        self::initConfig();
        self::init();
    }
    
    /**
     * 获取初始化配置
     */
    public static function initConfig()
    {
        self::$clientMap = config('im.base');
        self::$clientType = array_keys(self::$clientMap);
    }
    
    /**
     * 启动初始化
     */
    public static function init(){}
    
    /**
     * 当客户端发来消息时触发
     * @param string $client_id 连接id
     * @param mixed $message 具体消息
     * @return false|void
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function handleMessage(string $client_id, $message)
    {
        if (!self::getMessage($client_id, $message, $msg)) {
            return false;
        }
        // 如果没有$_SESSION['uid']说明客户端没有登录
        if(empty($_SESSION['uid']) || empty($_SESSION['userinfo']) || empty($_SESSION['isLogin']))
        {
            // 消息类型不是登录视为非法请求，关闭连接
            if($msg->type !== 'login')
            {
                self::close($client_id, '非法请求，关闭连接');
                return false;
            }
        }
        var_dump($_SESSION);
        switch ($msg->type) {
            // 连接
            case 'connect':
                // self::connect($client_id, $msg);
                break;
            // 登录
            case 'login':
                self::login($client_id, $msg);
                break;
            // 心跳
            case 'ping':
                self::ping($client_id, $msg);
                break;
            // 发送
            case 'send':
                self::send($client_id, $msg);
                break;
            // 退出
            case 'logout':
                self::logout($client_id, $msg);
                break;
            // 其他
            default:
                self::close($client_id);
        }
    }
    
    /**
     * 发送消息
     * @param string $client_id 连接id
     * @param $msg
     * @return bool|void
     */
    public static function send(string $client_id, $msg)
    {
        $uid = static::getUid();
        if (empty($uid)) {
            self::close($client_id, '登录消息过期！请重新连接并登录');
            return false;
        }
        $sendData = [
            'from_type' => self::getClientMapValue($msg->clientType, 'from_type'),
            // 'from_id' => $msg->from_id,
            'from_id' => $uid,
            'to_type' =>  self::getClientMapValue($msg->clientType, 'to_type'),
            'to_id' => $msg->to_id,
            'content' => $msg->content,
            'type' => $msg->msgType ?? 1
        ];
        // 保存消息
        $mm = MessageModel::sendSave($sendData);
        // 发送方会话消息列表数据
        $rsmOwn = [
            'content' => $msg->content,
            'own_id' => $uid,
            'own_type' => self::getClientMapValue($msg->clientType, 'from_type'),
            'other_id' => $msg->to_id,
            'other_type' => self::getClientMapValue($msg->clientType, 'to_type')
        ];
        // 接收方会话列表数据
        $rsmOther = [
            'content' => $msg->content,
            'own_id' => $msg->to_id,
            'own_type' => self::getClientMapValue($msg->clientType, 'to_type'),
            'other_id' => $uid,
            'other_type' => self::getClientMapValue($msg->clientType, 'from_type')
        ];
        
        $toId = self::getClientMapValue($msg->clientType, 'to_type') . 'App_' . $msg->to_id;
        var_dump('$fromId');
        var_dump($msg->clientType . '_' . $uid);
        var_dump('$toId');
        var_dump($toId);
        // 判断是否在线
        if (empty(Gateway::isUidOnline($toId))) {
             // 不在线 记录消息数量
            $rsmOther['num'] = 1;
        } else {
            // 在线 发送给接收方
            Gateway::sendToUid($toId, static::messageStructure($client_id, $uid, $mm->append(['from', 'to'])->toArray(), $msg->to_id));
        }
        // 保存发送方会话列表
        RecentSessionModel::sendSave($rsmOwn);
        // 保存接收方会话列表
        RecentSessionModel::sendSave($rsmOther);
        // 响应发送方
        Gateway::sendToCurrentClient(self::json('send', $client_id, '发送成功'));
    }
    
    /**
     * 解析消息数据
     * @param string $client_id 客户端id
     * @param string $message 消息字符串
     * @param bool $isReturnArr 是否返回数组
     * @return mixed
     */
    public static function getMessage(string $client_id, string $message, &$msg, bool $isReturnArr = false)
    {
        $msg = json_decode($message, $isReturnArr);
        return self::check($client_id, $msg);
    }
    
    /**
     * 检测 校验
     * @param string $client_id
     * @param $msg
     * @return false|void
     */
    public static function check(string $client_id, &$msg)
    {
        if (empty($msg->clientType) || !in_array($msg->clientType, self::$clientType)) {
            self::close($client_id, '未知客户端类型');
            return false;
        }
        if (empty($msg->token)) {
            self::close($client_id, '未知token');
            return false;
        }
        return self::getDecrypted($client_id, $msg);
    }
    
    /**
     * 获取某个值
     * @param $clientType
     * @param $keyName
     * @return string
     */
    public static function getClientMapValue($clientType, $keyName): string
    {
        return self::$clientMap[$clientType][$keyName];
    }
    
    /**
     * 校验token 并 解密
     * @param string $client_id
     * @param $msg
     * @return array|false
     */
    public static function getDecrypted(string $client_id, &$msg)
    {
        $response = getDecrypted($msg->token, self::getClientMapValue($msg->clientType, 'secretKeyPrefix'));
        if (empty($response)) {
            self::close($client_id, 'token非法');
            return false;
        }
        
        $response['uid'] = $response[self::getClientMapValue($msg->clientType, 'uidPk')];
        $response['im_uid'] = $msg->clientType . '_' . $response[self::getClientMapValue($msg->clientType, 'uidPk')];
        $msg->{$msg->clientType} = $response;

        return true;
    }
    
    /**
     * 获取uid
     */
    public static function getUid()
    {
        return $_SESSION['uid'] ?? 0;
    }
    
    /**
     * 获取用户信息
     */
    public static function getUserInfo()
    {
        return $_SESSION['userinfo'];
    }
    
    /**
     * 查询用户信息
     * @param string $tableName
     * @param int $uid
     * @return array|Model|null
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function getInfo(string $tableName, int $uid)
    {
        return Db::table($tableName)->find($uid);
    }
    
    /**
     * 登录
     * @param string $client_id
     * @param $msg
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function login(string $client_id, $msg): bool
    {
        // 标记该客户端已经登录
        $loginInfo['isLogin'] = true;
        // 设置uid
        $loginInfo['uid'] = $msg->{$msg->clientType}['uid'];
        $loginInfo['im_uid'] = $msg->{$msg->clientType}['im_uid'];
        // 设置 imType
        $loginInfo['imType'] = self::getClientMapValue($msg->clientType, 'imType');
        // 保存用户信息
        $loginInfo['userinfo'] = self::getInfo(self::getClientMapValue($msg->clientType, 'tableName') , $msg->{$msg->clientType}['uid']);
        // 设置session
        $_SESSION = $loginInfo;
        var_dump('im_uid');
        var_dump($loginInfo['im_uid']);
        // 将对应 client_id 和 uid 进行绑定
        self::bindUid($client_id, $loginInfo['im_uid']);
        return Gateway::sendToCurrentClient(self::json('login', $client_id, '登录成功'));
    }
    
    /**
     * 绑定 uid
     * @param string $client_id
     * @param $uid
     */
    public static function bindUid(string $client_id, $uid)
    {
        Gateway::bindUid($client_id, $uid);
    }
    
    /**
     * 解除 uid 绑定
     * @param string $client_id
     * @param $uid
     */
    public static function unbindUid(string $client_id, $uid)
    {
        Gateway::unbindUid($client_id, $uid);
    }
    
    /**
     * 心跳校验 回复
     * @param string $client_id
     * @param $msg
     * @return bool|void
     */
    public static function ping(string $client_id, $msg)
    {
        // return Gateway::sendToClient($client_id, self::json('pong', $client_id, '心跳正常'));
    }
    
    /**
     * 退出 断开连接
     * @param string $client_id
     */
    public static function logout(string $client_id)
    {
        Gateway::sendToCurrentClient(self::json('close', $client_id, '退出成功，已断开连接...'));
        Gateway::closeClient($client_id);
    }
    
    /**
     * 主动关闭客户端连接并发送消息
     * @param string $client_id 客户端id
     * @param mixed $msg 消息
     */
    public static function close(string $client_id, $msg = null): bool
    {
        Gateway::closeClient($client_id, self::json('close', $client_id, $msg ?? '未知连接，即将断开连接...', [], 110));
        return false;
    }
    
    /**
     * 日志输出
     * @param $log
     */
    public static function log($log)
    {
        var_dump(static::getUid());
        // var_dump($log);
    }
    
    /**
     * json数据
     * @param string $type 消息类型
     * @param string $client_id 发送的客户端id
     * @param string $msg 消息内容
     * @param mixed  $data 数据
     * @return false|string
     */
    public static function json(string $type, string $client_id, string $msg, $data = null, $code = 200)
    {
        $res = [
            'type'      => $type,
            'client_id' => $client_id,
            'code'      => $code,
            'msg'       => $msg,
            'data'      => $data,
            'time'      => time()
        ];
        self::log($res);
        return json_encode($res);
    }
    
    /**
     * 消息结构体
     * @param string $client_id 客户端id
     * @param int $from_id 发送方id
     * @param array $msg 消息
     * @param int $to_id 接收方id
     * @param null $data 其他数据
     * @return false|string
     */
    public static function messageStructure(string $client_id, int $from_id, array $msg, int $to_id, $data = null)
    {
        $res = [
            'type'      => 'msg',
            'client_id' => $client_id,
            'code'      => 200,
            'from_id'   => $from_id,
            'msg'       => $msg,
            'to_id'     => $to_id,
            'data'      => $data,
            'time'      => time()
        ];
        self::log($res);
        return json_encode($res);
    }
}